const express = require('express');
const socketIo = require('socket.io');
const cors = require('cors');
const morgan = require('morgan');
const { pool, initRedis } = require('./config/database');
const cron = require('node-cron');
const dayjs = require("dayjs")
const http = require('http');
// 初始化Express应用
const app = express();
const server = http.createServer(app);
// 创建WebScoket服务器
const io = socketIo(server, {
  cors: {
    origin: "http://192.168.1.3:8080", // 你的前端地址
    origin: '*',
    methods: ['GET', 'POST']
  }
});

// 中间件配置
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(morgan('dev'));

// 静态文件服务
app.use(express.static('public'));

// 路由配置
app.use('/api/auth', require('./routes/auth'));
app.use('/api/books', require('./routes/books'));
app.use("/api/system", require("./routes/admin"))
app.use('/api/borrows', require('./routes/borrows'));

app.use('/api/users', require('./routes/users'));
app.use('/api/messages', require('./routes/messages'));
app.use('/api/statistics', require('./routes/statistics'));
app.use('/api/latest-books', require('./routes/latest-books'));

app.use('/api/chats', require('./routes/chats'));

// 版本一：通过两个人的id创建一个唯一的房间号，通过群聊的方式进行一对一聊天
// Socket.io 配置
// io.on('connection', (socket) => {
//   console.log('A user connected:', socket.id);

//   // 加入私聊房间（由两个用户ID生成的唯一房间名）
//   socket.on('join_private_chat', ({ userId, targetUserId }) => {
//     const roomName = getPrivateRoomName(userId, targetUserId);
//     socket.join(roomName);
//     console.log(`用户 ${userId} 加入了私聊房间: ${roomName}`);
//   });

//   // 处理私聊消息
//   socket.on('private_message', async (data) => {
//     const { senderId, receiverId, content } = data;
//     console.log("data++++++", data)
//     const roomName = getPrivateRoomName(senderId, receiverId);

//     // 发送消息给房间内的所有客户端（即私聊双方）
//     io.to(roomName).emit('new_private_message', {
//       senderId,
//       content,
//       timestamp: new Date()
//     });

//     // 存储消息到数据库
//     try {
//       const newTime = dayjs().format("YYYY-MM-DD HH:mm:ss");

//       await pool.execute(
//         'INSERT INTO messages (sender_id, receiver_id, content, created_at) VALUES (?, ?, ?, ?)',
//         [senderId, receiverId, content, newTime]
//       );

//       console.log("私聊消息存储成功");
//     } catch (error) {
//       console.error('存储私聊消息失败:', error);
//     }
//   });

//   socket.on('disconnect', () => {
//     console.log(`用户断开连接: ${socket.userId || '未知用户'} (socket ID: ${socket.id})`);
//   });
// });
// 生成唯一的私聊房间名
// function getPrivateRoomName(userId1, userId2) {
//   const sortedIds = [userId1, userId2].sort();
//   return `private_${sortedIds[0]}_${sortedIds[1]}`;
// }

// 版本二：完全实现一对一聊天，通过map存储socketid分辨id
// const userSocketMap = new Map(); // 一个map对象
// const userHeartbeats = new Map(); // userId => lastTime
// io.on("connection", async (socket) => {
//   // 与用户的id进行关联
//   // socket.on('login', ({ userId, csId }) => {
//   //   console.log("userId, csIduserId, csId",userId, csId, socket.id)
//   //   socket.userId = userId;
//   //   socket.csId = csId;
//   //   userSocketMap.set(userId, socket.id);
//   //   const room = `room-${csId}`
//   //   socket.join(room);
//   //   console.log(`用户 ${userId} 登录，加入房间 room-${csId}`);
//   // });
//   // 优化登录操作
//   socket.on('login', ({ userId, csId }) => {
//     // 添加参数验证
//     if (!userId || !csId) {
//       console.error('缺少必要参数: userId 或 csId');
//       return;
//     }

//     // 确保userId是字符串（如果数据库存储的是数字）
//     userId = String(userId);
//     csId = String(csId);

//     socket.userId = userId;
//     socket.csId = csId;

//     // 打印调试信息
//     console.log(`用户登录: userId=${userId}, csId=${csId}, socketId=${socket.id}`);
//     // 广播给所有客户端（包括断连用户自己）
//     io.emit('user_online', userId); // 👈 新增

//     // 把所有在线的用户返回给前端
//     io.emit('Online_user', Array.from(userSocketMap.entries()))
//     // 如果有用户上线，通知所有房间更新列表
//     if (socket.id) {
//       io.emit('update_member_list'); // 全局通知（或按房间通知）
//     }

//     // 更新映射
//     userSocketMap.set(userId, socket.id);

//     // 加入房间
//     const room = `room-${csId}`;
//     socket.join(room);
//     console.log(`用户 ${userId} 加入房间 ${room}`);

//     // 打印当前Map状态
//     console.log('当前用户映射:', Array.from(userSocketMap.entries()));
//   });

//   // 获取房间内所有的用户
//   socket.on('all_member', async () => {
//     const room = `room-${socket.csId}`;
//     const sockets = await io.in(room).fetchSockets();
//     const users = sockets.map(s => s.userId);

//     console.log("后端：指定房间的用户", users)

//     // Check if there are any users to query
//     if (users.length === 0) {
//       return socket.emit('myUsersList', []);
//     }

//     let sql = `
//         SELECT 
//             u.id, 
//             u.role, 
//             u.username, 
//             u.phone,
//             COUNT(m.id) AS message_count
//         FROM 
//             users u
//         LEFT JOIN 
//             messages m ON u.id = m.sender_id AND m.receiver_id = ? AND m.read_at IS NULL
//         WHERE 
//             u.id IN (?) AND
//             u.role NOT IN ('customer_service', 'technical_support')
//         GROUP BY
//             u.id, u.role, u.username, u.phone
//     `;

//     try {
//       const [results] = await pool.query(sql, [socket.userId, users]);
//       socket.emit('myUsersList', results);
//     } catch (error) {
//       console.error('Database query error:', error);
//       socket.emit('myUsersList', []);
//     }
//   });

//   // 与用户进行私聊
//   socket.on("private_message", async (data) => {
//     const { senderId, receiverId, content } = data;

//     // 确保ID是字符串
//     const normalizedReceiverId = String(receiverId);

//     console.log('当前用户映射:', Array.from(userSocketMap.entries()));
//     console.log(`查找接收者: ${normalizedReceiverId}`);

//     const receiverSocketId = userSocketMap.get(normalizedReceiverId);
//     console.log("找到的接收者socket ID:", receiverSocketId);

//     if (!receiverSocketId) {
//       console.log(`用户 ${receiverId} 不在线，消息将仅存入数据库`);
//     } else {
//       io.to(receiverSocketId).emit('new_private_message', {
//         senderId: senderId,
//         content: content,
//         timestamp: new Date()
//       });
//       console.log(`用户${senderId}的消息已实时发送给用户 ${receiverId},发送的内容：${content}`);
//     }

//     // 存储消息到数据库
//     try {
//       const newTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
//       await pool.execute(
//         'INSERT INTO messages (sender_id, receiver_id, content, created_at) VALUES (?, ?, ?, ?)',
//         [senderId, receiverId, content, newTime]
//       );
//     } catch (error) {
//       console.error('存储私聊消息失败:', error);
//     }
//   });

//   // 断开连接
//   socket.on('disconnect', () => {
//     const disconnectedUserId = socket.userId; // 假设已存储用户ID

//     // 删除映射关系
//     userSocketMap.delete(disconnectedUserId);

//     // 如果有用户离线，通知所有房间更新列表
//     if (disconnectedUserId) {
//       io.emit('update_member_list'); // 全局通知（或按房间通知）
//     }

//     // 广播给所有客户端（包括断连用户自己）
//     io.emit('user_offline', disconnectedUserId); // 👈 新增
//     console.log(`用户断开连接: ${socket.userId || '未知用户'} (socket ID: ${socket.id})`);
//   });

//   // 心跳测试的触发事件
//   socket.on('heartbeat', () => {
//     if (socket.userId) {
//       userHeartbeats.set(socket.userId, Date.now());
//     }
//   });

// })
// // 打印所有连接的调试信息
// setInterval(() => {
//   console.log('\n当前活跃连接状态:');
//   console.log('用户映射:', Array.from(userSocketMap.entries()));

//   io.of('/').sockets.forEach((socket) => {
//     console.log(`Socket ID: ${socket.id}, UserID: ${socket.userId}, CSID: ${socket.csId}`);
//   });
// }, 30000); // 每30秒打印一次

// // 心跳测试，定时测试用户是否还在线
// setInterval(() => {
//   const now = Date.now();
//   let hasUserOffline = false; // 标记是否有用户离线

//   // 检测心跳超时的用户
//   for (const [userId, lastTime] of userHeartbeats.entries()) {
//     if (now - lastTime > 4000) {
//       userHeartbeats.delete(userId);
//       userSocketMap.delete(userId);
//       console.log(`用户 ${userId} 心跳超时，视为离线`);
//       hasUserOffline = true; // 标记状态变化
//     }
//   }
//   // 如果有用户离线，通知所有房间更新列表
//   if (hasUserOffline) {
//     io.emit('update_member_list'); // 全局通知（或按房间通知）
//   }
// }, 2000);

// 版本三：对上面的代码进行优化----缩减版
const userSocketMap = new Map(); // 一个map对象
const userHeartbeats = new Map(); // userId => lastTime
io.on("connection", async (socket) => {
  // 当新客户端连接时打印日志
  console.log(`现在登录的用户socketid: ${socket.id}`);
  // 获取客服的列表方法
  const get_server_list = async () => {
    try {
      let sql_server = `
            SELECT 
                u.id, 
                u.role, 
                u.username, 
                u.phone,
                COUNT(m.id) AS message_count
            FROM 
                users u
            LEFT JOIN 
                messages m ON u.id = m.sender_id AND m.receiver_id = ? AND m.read_at IS NULL
            WHERE 
                u.role IN ('customer_service', 'technical_support')
            GROUP BY
                u.id, u.role, u.username, u.phone
        `
      const [results] = await pool.query(sql_server, [socket.userId])

      let firstElements = Array.from(userSocketMap.entries()).map(([key, value]) => key);
      const onlineServerids = firstElements.map(Number);
      console.log("所有在线用户的id:", onlineServerids); // [2, 1]
      const filterServer = new Set(onlineServerids); // 去重+has方法
      // 对返回结果进行在线状态处理
      const new_results = results.map((item) => {
        if (item.role == "customer_service") {
          return {
            ...item,
            name: "图书借阅咨询",
            describe: "图书借阅相关问题咨询",
            isOnline: filterServer.has(item.id),
          };
        }
        if (item.role == "technical_support") {
          return {
            ...item,
            name: "技术支持",
            describe: "系统使用问题咨询",
            isOnline: filterServer.has(item.id),
          };
        }
      });
      // 把所有在线的客服返回给前端---当用用户触发这个方法的时候发送这个事件
      io.emit('server_list_new', new_results)
    } catch (error) {
      console.error('客服的数据库报错了：', error);
    }
  }
  // 获取用户的列表方法
  const get_user_list = async () => {
    // 获取某个房间中的用户的id
    // const sockets = await io.in(room).fetchSockets();
    // const users = sockets.map(s => s.userId);
    // const set_users = new Set(users)
    // 获取map对象中在线的用户的id
    let firstElements = Array.from(userSocketMap.entries()).map(([key, value]) => key);
    const onlineServerids = firstElements.map(Number);
    console.log("所有在线用户的id:", onlineServerids); // [2, 1]
    const set_users = new Set(onlineServerids)
    let sql = `
        SELECT 
                u.id, 
                u.role, 
                u.username, 
                u.phone,
                COUNT(m.id) AS message_count
            FROM 
                users u
            LEFT JOIN 
                messages m ON u.id = m.sender_id AND m.receiver_id = ? AND m.read_at IS NULL
            WHERE 
                u.role NOT IN ('customer_service', 'technical_support')
            GROUP BY
                u.id, u.role, u.username, u.phone
    `;

    try {
      const [results] = await pool.query(sql, [socket.userId]);
      const new_resluts = results.map((item) => {
        return {
          ...item,
          name: "用户",
          isOnline: set_users.has(Number(item.id)), // 动态设置状态
        }
      })
      socket.emit('user_list_new', new_resluts);
    } catch (error) {
      console.error('用户的数据库报错了：', error);
      socket.emit('user_list_new', []);
    }
  }
  // 获取聊天记录的方法
  const get_msg_list = async (receiverId, senderId) => {
    try {
      let sql = `
            SELECT 
                m.receiver_id as receiverId,
                m.sender_id as senderId,
                m.content,
                m.created_at
            FROM 
                messages m
            WHERE 
                m.system = 'false' AND (m.receiver_id = ? AND m.sender_id = ?) OR 
				(m.receiver_id = ? AND m.sender_id = ?)
        `
      const [results] = await pool.query(sql, [receiverId, senderId, senderId, receiverId])
      // console.log('获取私聊消息成功:', results);
      // 发送消息给发送者(自己)
      socket.emit('msg_list', results);
      // 广播给接收者
      io.to(receiverSocketId).emit('msg_list', results);
    } catch (error) {
      console.error('获取私聊消息失败:', error);
    }
  }

  // 优化登录操作
  socket.on('login', async ({ userId, csId }) => {
    // 添加参数验证
    if (!userId || !csId) {
      console.error('缺少必要参数: userId 或 csId');
      return;
    }

    // 确保userId是字符串（如果数据库存储的是数字）
    userId = String(userId);
    csId = String(csId);

    socket.userId = userId;  // 当前用户的id
    socket.csId = csId; // 聊天对象的id

    // 打印调试信息
    console.log(`用户登录: userId=${userId}, csId=${csId}, socketId=${socket.id}`);

    // 更新映射
    userSocketMap.set(userId, socket.id);

    // 加入房间----把在同一个客服的人拉进一个房间，前端需要判断：如果当前用户是客服的话csId是他自己的id
    const room = `room-${csId}`;
    socket.join(room);
    console.log(`用户 ${userId} 加入房间 ${room}`);

    // 与数据库进行联系----用户列表---一个房间中的用户
    get_user_list()

    // 客服列表
    get_server_list()

    // 打印当前Map状态
    console.log('当前用户映射:', Array.from(userSocketMap.entries()));
  });

  // 与用户进行私聊
  socket.on("private_message", async (data) => {
    const { senderId, receiverId, content } = data;

    // 确保ID是字符串
    const normalizedReceiverId = String(receiverId);

    console.log('当前用户映射:', Array.from(userSocketMap.entries()));
    console.log(`查找接收者: ${normalizedReceiverId}`);

    const receiverSocketId = userSocketMap.get(normalizedReceiverId);
    console.log("找到的接收者socket ID:", receiverSocketId);

    if (!receiverSocketId) {
      console.log(`用户 ${receiverId} 不在线，消息将仅存入数据库`);
    } else {
      // 发送消息给发送者(自己)
      socket.emit('new_private_message', {
        senderId: senderId,
        content: content,
        timestamp: new Date(),
        self: true            // 标记为自己发送的消息
      });
      // 广播给接收者
      io.to(receiverSocketId).emit('new_private_message', {
        senderId: senderId,
        content: content,
        timestamp: new Date(),
        self: false
      });

      console.log(`用户${senderId}的消息已实时发送给用户 ${receiverId},发送的内容：${content}`);
    }

    // 存储消息到数据库---持久化
    try {
      const newTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
      await pool.execute(
        'INSERT INTO messages (sender_id, receiver_id, content, created_at) VALUES (?, ?, ?, ?)',
        [senderId, receiverId, content, newTime]
      );
    } catch (error) {
      console.error('存储私聊消息失败:', error);
    }
    // 之后，我还需要获取聊天的历史记录
    get_msg_list(receiverId, senderId) 
  });

  // 注册一个获取聊天记录的事件---主动获取记录
  socket.on('his_msg_list', ({ receiverId, senderId }) => {
    get_msg_list(receiverId, senderId)
  })

  // 用户标记消息为已读----在切换分类的时候调用
  socket.on('set_msg_read', async (data) => {
    const { server_id, user_id } = data
    const newTime = dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss")
    try {
      let sql = `
            UPDATE messages m SET m.read_at = ?
            WHERE 
                m.read_at IS NULL AND m.receiver_id = ? AND m.sender_id = ?
        `
      const [results] = await pool.query(sql, [newTime, Number(user_id), server_id])
      console.log("标记消息为已读成功：", results)
      // 标记为已读后需要重新调用列表方法
      // 重新发送列表事件
      get_server_list() // 客服的
      get_user_list() // 用户的
    } catch (error) {
      console.error("标记消息为已读失败：", error)
    }
  })

  // 断开连接
  socket.on('disconnect', () => {
    const disconnectedUserId = socket.userId; // 假设已存储用户ID

    // 删除映射关系
    userSocketMap.delete(disconnectedUserId);
    // 重新发送列表事件
    get_server_list() // 客服的
    get_user_list() // 用户的
    console.log(`用户断开连接: ${socket.userId || '未知用户'} (socket ID: ${socket.id})`);
  });

  // 心跳测试的触发事件
  socket.on('heartbeat', () => {
    if (socket.userId) {
      userHeartbeats.set(socket.userId, Date.now());
    }
  });

})
// 打印所有连接的调试信息
setInterval(() => {
  console.log('\n当前活跃连接状态:');
  console.log('用户映射:', Array.from(userSocketMap.entries()));

  io.of('/').sockets.forEach((socket) => {
    console.log(`Socket ID: ${socket.id}, UserID: ${socket.userId}, CSID: ${socket.csId}`);
  });
}, 30000); // 每30秒打印一次

// 心跳测试，定时测试用户是否还在线
setInterval(() => {
  const now = Date.now();
  let hasUserOffline = false; // 标记是否有用户离线

  // 检测心跳超时的用户
  for (const [userId, lastTime] of userHeartbeats.entries()) {
    if (now - lastTime > 4000) {
      userHeartbeats.delete(userId);
      userSocketMap.delete(userId);
      console.log(`用户 ${userId} 心跳超时，视为离线`);
      hasUserOffline = true; // 标记状态变化
    }
  }
  // 如果有用户离线，通知所有房间更新列表
  if (hasUserOffline) {
    console.log("如果有用户离线，通知所有房间更新列表")
    // 重新发送列表事件
    get_server_list() // 客服的
    get_user_list() // 用户的       
  }
}, 60000);



// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ message: '服务器内部错误' });
});
// 启动时检查逾期书籍
async function checkOverdueBooksOnStartup() {
  try {
    const [result] = await pool.execute(`
          UPDATE borrows 
          SET status = 'overdue' 
          WHERE status != 'returned' 
          AND due_date < NOW() 
          AND status != 'overdue'
      `);
    console.log(`[启动任务] 更新了 ${result.affectedRows} 条逾期记录`);
  } catch (error) {
    console.error('[启动任务] 逾期检查出错:', error);
  }
}
// 每天凌晨1点检查逾期记录
cron.schedule('0 1 * * *', async () => {
  checkOverdueBooksOnStartup(); // 确保数据库连接就绪后执行
})
// 数据库连接成功后调用
pool.getConnection((err, connection) => {
  if (err) throw err;
  connection.release();
  checkOverdueBooksOnStartup(); // 确保数据库连接就绪后执行
});
// 启动服务器
const PORT = process.env.PORT || 3000;


server.listen(PORT, async () => {
  try {
    await initRedis(); // 初始化Redis连接、方便项目使用
    console.log('Redis connected successfully');
    console.log(`Server is running on port ${PORT}`);
  } catch (error) {
    console.error('Failed to connect to Redis:', error);
    process.exit(1);
  }
});